<?php
/**
 *  This file is part of amfPHP
 *
 * LICENSE
 *
 * This source file is subject to the license that is bundled
 * with this package in the file license.txt.
 * @package Amfphp_Core
 */


/**
 * where everything comes together in amfphp.
 * The class used for the entry point of a remoting call
 *
 * @package Amfphp_Core
 * @author Ariel Sommeria-klein
 */
class Amfphp_Core_Gateway {

	/**
	 * filter just after plugin init. Use this to add a service folder for a plugin
	 * @param array serviceFolderPaths array of absolute paths
	 */
	const FILTER_SERVICE_FOLDER_PATHS = 'FILTER_SERVICE_FOLDER_PATHS';

	/**
	 * filter just after plugin init. Use this to add a service for a plugin when a service folder isn't enough
	 * @param array serviceNames2ClassFindInfo array of ClassFindInfo. Key is the service nane
	 */
	const FILTER_SERVICE_NAMES_2_CLASS_FIND_INFO = 'FILTER_SERVICE_NAMES_2_CLASS_FIND_INFO';

	/**
	 * filter called when the serialized request comes in.
	 * @todo this filter only allows manipulation of raw post data, and is as such a bit misleading. Maybe rename and do filters for GET and POST
	 * @param String $rawData the raw http data
	 */
	const FILTER_SERIALIZED_REQUEST = 'FILTER_SERIALIZED_REQUEST';

	/**
	 * filter called to allow a plugin to override the default amf deserializer.
	 * Plugin should return a Amfphp_Core_Common_IDeserializer if it recognizes the content type
	 * @param Amfphp_Core_Common_IDeserializer $deserializer the deserializer. null at call in gateway.
	 * @param String $contentType
	 */
	const FILTER_DESERIALIZER = 'FILTER_DESERIALIZER';

	/**
	 * filter called after the request is deserialized. The callee can modify the data and return it.
	 * @param mixed $deserializedRequest
	 */
	const FILTER_DESERIALIZED_REQUEST = 'FILTER_DESERIALIZED_REQUEST';

	/**
	 * filter called to allow a plugin to override the default amf deserialized request handler.
	 * Plugin should return a Amfphp_Core_Common_IDeserializedRequestHandler if it recognizes the request
	 * @param Amfphp_Core_Common_IDeserializedRequestHandler $deserializedRequestHandler null at call in gateway.
	 * @param String $contentType
	 */
	const FILTER_DESERIALIZED_REQUEST_HANDLER = 'FILTER_DESERIALIZED_REQUEST_HANDLER';

	/**
	 * filter called when the response is ready but not yet serialized.  The callee can modify the data and return it.
	 * @param $deserializedResponse
	 */
	const FILTER_DESERIALIZED_RESPONSE = 'FILTER_DESERIALIZED_RESPONSE';

	/**
	 * filter called to allow a plugin to override the default amf exception handler.
	 * If the plugin takes over the handling of the request message,
	 * it must set this to a proper Amfphp_Core_Common_IExceptionHandler
	 * @param Amfphp_Core_Common_IExceptionHandler $exceptionHandler. null at call in gateway.
	 * @param String $contentType
	 */
	const FILTER_EXCEPTION_HANDLER = 'FILTER_EXCEPTION_HANDLER';

	/**
	 * filter called to allow a plugin to override the default amf serializer.
	 * @param Amfphp_Core_Common_ISerializer $serializer the serializer. null at call in gateway.
	 * @param String $contentType
	 * Plugin sets to a Amfphp_Core_Common_ISerializer if it recognizes the content type
	 */
	const FILTER_SERIALIZER = 'FILTER_SERIALIZER';

	/**
	 * filter called when the packet response is ready and serialized.
	 * @param String $rawData the raw http data
	 */
	const FILTER_SERIALIZED_RESPONSE = 'FILTER_SERIALIZED_RESPONSE';

	/**
	 * filter called to get the headers
	 * @param array $headers an associative array of headers. For example array('Content-Type' => 'application/x-amf')
	 * @param String $contentType
	 */
	const FILTER_HEADERS = 'FILTER_HEADERS';


	/**
	 * config.
	 * @var Amfphp_Core_Config
	 */
	protected $config;

	/**
	 * typically the $_GET array.
	 * @var array
	 */
	protected $getData;

	/**
	 * typically the $_POST array.
	 * @var array
	 */
	protected $postData;

	/**
	 * the content type. For example for amf, application/x-amf
	 * @var String
	 */
	protected $contentType;

	/**
	 * the serialized request
	 * @var String
	 */
	protected $rawInputData;

	/**
	 * the serialized response
	 * @var String
	 */
	protected $rawOutputData;

	/**
	 *
	 */
	/**
	 * constructor
	 * @param array $getData typically the $_GET array.
	 * @param array $postData typically the $_POST array.
	 * @param String $rawInputData
	 * @param String $contentType
	 * @param Amfphp_Core_Config $config optional. The default config object will be used if null
	 */
	public function  __construct(array $getData, array $postData, $rawInputData, $contentType, Amfphp_Core_Config $config = null) {
		$this->getData = $getData;
		$this->postData = $postData;
		$this->rawInputData = $rawInputData;
		$this->contentType = $contentType;
		if($config){
			$this->config = $config;
		}else{
			$this->config = new Amfphp_Core_Config();
		}

	}

	/**
	 * The service method runs the gateway application.  It deserializes the raw data passed into the constructor as an Amfphp_Core_Amf_Packet, handles the headers,
	 * handles the messages as requests to services, and returns the responses from the services
	 * It does not however handle output headers, gzip compression, etc. that is the job of the calling script
	 *
	 * @return <String> the serialized amf packet containg the service responses
	 */
	public function service(){
		$filterManager = Amfphp_Core_FilterManager::getInstance();
		$defaultHandler = new Amfphp_Core_Amf_Handler($this->config->sharedConfig);
		$deserializedResponse = null;

		try{
			Amfphp_Core_PluginManager::getInstance()->loadPlugins($this->config->pluginsFolders, $this->config->pluginsConfig, $this->config->sharedConfig, $this->config->disabledPlugins);

			//filter service folder paths
			$this->config->serviceFolderPaths = $filterManager->callFilters(self::FILTER_SERVICE_FOLDER_PATHS, $this->config->serviceFolderPaths);

			//filter service names 2 class find info
			$this->config->serviceNames2ClassFindInfo = $filterManager->callFilters(self::FILTER_SERVICE_NAMES_2_CLASS_FIND_INFO, $this->config->serviceNames2ClassFindInfo);

			//filter serialized request
			$this->rawInputData = $filterManager->callFilters(self::FILTER_SERIALIZED_REQUEST, $this->rawInputData);

			//filter deserializer
			$deserializer = $filterManager->callFilters(self::FILTER_DESERIALIZER, $defaultHandler, $this->contentType);

			//deserialize
			$deserializedRequest = $deserializer->deserialize($this->getData, $this->postData, $this->rawInputData);

			//filter deserialized request
			$deserializedRequest = $filterManager->callFilters(self::FILTER_DESERIALIZED_REQUEST, $deserializedRequest);

			//create service router
			$serviceRouter = new Amfphp_Core_Common_ServiceRouter($this->config->serviceFolderPaths, $this->config->serviceNames2ClassFindInfo, $this->config->checkArgumentCount);

			//filter deserialized request handler
			$deserializedRequestHandler = $filterManager->callFilters(self::FILTER_DESERIALIZED_REQUEST_HANDLER, $defaultHandler, $this->contentType);
			//handle request
			$deserializedResponse = $deserializedRequestHandler->handleDeserializedRequest($deserializedRequest, $serviceRouter);

		}catch(Exception $exception){
			//filter exception handler
			$exceptionHandler = $filterManager->callFilters(self::FILTER_EXCEPTION_HANDLER, $defaultHandler, $this->contentType);

			//handle exception
			$deserializedResponse = $exceptionHandler->handleException($exception);

		}
		//filter deserialized response
		$deserializedResponse = $filterManager->callFilters(self::FILTER_DESERIALIZED_RESPONSE, $deserializedResponse);


		//filter serializer
		$serializer = $filterManager->callFilters(self::FILTER_SERIALIZER, $defaultHandler, $this->contentType);

		//serialize
		$this->rawOutputData = $serializer->serialize($deserializedResponse);

		//filter serialized response
		$this->rawOutputData = $filterManager->callFilters(self::FILTER_SERIALIZED_RESPONSE, $this->rawOutputData);

		return $this->rawOutputData;

	}

	/**
	 * get the response headers. Creates an associative array of headers, then filters them, then returns an array of strings
	 * @return array
	 */
	public function getResponseHeaders(){

		$filterManager = Amfphp_Core_FilterManager::getInstance();
		$headers = array('Content-Type' => $this->contentType);
		$headers = $filterManager->callFilters(self::FILTER_HEADERS, $headers, $this->contentType);
		$ret = array();
		foreach($headers as $key => $value){
			$ret[] = $key . ': ' . $value;
		}
		return $ret;
	}

	/**
	 * helper function for sending gateway data to output stream
	 */
	public function output(){

		$responseHeaders = $this->getResponseHeaders();
		foreach($responseHeaders as $header){
			header($header);
		}
		echo $this->rawOutputData;
	}

}
?>
